package com.logic.landseaserver.models.payment.channel;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.gson.JsonObject;
import com.logic.common.security.SecurityUtils;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.PaymentResultCode;
import com.logic.landseaserver.common.util.CommonUtils;
import com.logic.landseaserver.common.util.JsonObjectUtils;
import com.logic.landseaserver.common.util.LandSeaUtil;
import com.logic.landseaserver.common.util.SendHttpMsgHandler;
import com.logic.landseaserver.domain.BillPayment;
import com.logic.landseaserver.domain.Contract;
import com.logic.landseaserver.domain.ShiJiProjectConfig;
import com.logic.landseaserver.models.payment.PaymentConstant;
import com.logic.landseaserver.models.payment.PaymentConstant.BillPaymentStatus;
import com.logic.landseaserver.models.payment.PaymentConstant.PayBank;
import com.logic.landseaserver.models.payment.PaymentConstant.PayType;
import com.logic.landseaserver.models.payment.bean.PaymentHelperBean;
import com.logic.landseaserver.models.payment.bean.PaymentQueryRequest;
import com.logic.landseaserver.models.payment.bean.PaymentQueryResult;
import com.logic.landseaserver.models.payment.bean.PaymentRequest;
import com.logic.landseaserver.models.payment.bean.ShiJiPaymentResponse;
import com.logic.landseaserver.models.payment.config.ShijiPaymentConfig;
import com.logic.landseaserver.models.payment.service.ShiJiPaymentService;
import com.logic.landseaserver.models.payment.util.ShiJiSignVerifyUtil;
import com.logic.landseaserver.persistence.read.ContractReadMapper;
import com.logic.landseaserver.persistence.write.BillPaymentWriteMapper;

import net.sf.json.JSONObject;

/**
 * 
 *
 * 项目名称：system-server</br>
 * 类名称：ShiJiPosBillPaymentGenerator</br>
 * 类描述：银石POS支付</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年7月17日 下午11:11:19</br>
 * 
 * @version 1.0
 *
 */
@Service(PaymentConstant.GENETRATE_BILLPAYMENT_PREFIX + PaymentConstant.PayChannel.SHIJI)
public class ShiJiPaymentChannel extends AbstractPaymentChannel
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ShiJiPaymentChannel.class);
    
    /** 获取支付二维码成功码 **/
    private static final String SUCCESS_CODE = "00";
    
    @Autowired
    private ShijiPaymentConfig shijiPaymentConfig;
    
    @Autowired
    private ShiJiPaymentService shiJiPaymentService;
    
    @Autowired
    private ContractReadMapper contractReadMapper;
    
    @Autowired
    private BillPaymentWriteMapper billPaymentWriteMapper;
    
    @Override
    public BillPayment buildBillPayment(PaymentRequest paymentRequest)
        throws LandseaException
    {
        // 构造BillPayment
        BillPayment insertBillPayment = new BillPayment();
        insertBillPayment.setMoney(LandSeaUtil.get2Double(paymentRequest.getMoney()));
        insertBillPayment.setBank(PayBank.SHIJI);
        
        return insertBillPayment;
    }
    
    @Override
    public void sendPayRequest(BillPayment billPayment, PaymentHelperBean helperObject, Map<String, Object> resultMap)
        throws LandseaException
    {
        // 区分网页端和移动端
        if (PayType.SHIJI_MOBILE.equals(billPayment.getPayType()))
        {
            sendPayRequestMobile(billPayment, helperObject, resultMap);
        }
        else
        {
            sendPayRequestWeb(billPayment, helperObject, resultMap);
        }
    }
    
    /**
     * 
     * [简要描述]：移动应用内支付</br>
     * [详细描述]：</br>
     * [作者]：Administrator(2017-07-23)</br>
     *
     * @param billPayment
     * @param helperObject
     * @param resultMap
     * @throws LandseaException
     *
     */
    public void sendPayRequestMobile(BillPayment billPayment, PaymentHelperBean helperObject,
        Map<String, Object> resultMap)
        throws LandseaException
    {
        // 获取日志前缀
        String logInfo = getLog(billPayment);
        
        try
        {
            // 地址
            String postURL = shijiPaymentConfig.getMobileApiUrl();
            
            // 测试加密密钥
            String key = shijiPaymentConfig.getKey();
            
            // partnerId
            String partnerId = shijiPaymentConfig.getPartnerID();
            
            Map<String,Object> paramMap = new LinkedHashMap<String, Object>();
            
            paramMap.put("PartnerID", partnerId);
            
            paramMap.put("MerchantTxnNo", billPayment.getId());
            
            // 获取商户号
            String merchantId = getMerchantId(helperObject, true);
            paramMap.put("MerchantID", merchantId);
            
            DecimalFormat df = new DecimalFormat("0.00");
            String money = df.format(billPayment.getMoney());
            paramMap.put("TxnAmt", money);
            
            // 回调地址
            paramMap.put("NotifyUrl", shijiPaymentConfig.getCallbackUrl());
            
            // 签名
            String sign = ShiJiSignVerifyUtil.sign(JsonObjectUtils.map2JsonObject(paramMap), key);
            paramMap.put("Sign", sign);
            paramMap.put("postURL", postURL);
            
            LOGGER.info(logInfo + "发送应用内支付|生成签名|" + paramMap);
            
            // 将参数带到页面，只能用页面的form提交post请求
            resultMap.put("PayPostParam", paramMap);
            
            // 证书文件URL
            //URL certificateUrl = CommonUtils.getResourceUrl(shijiPaymentConfig.getCertificate());
            // 证书密码
            //String certificatePassword = shijiPaymentConfig.getCertificatePassword();
            
            // 将参数拼接到URL后面
            //postURL = SendHttpMsgHandler.assembleParam(postURL, paramMap);
            
            //LOGGER.info(logInfo + "发送应用内支付请求|URL=" + postURL);
            
            // 发送HTTPS消息,参数拼接在URL后面
            //String response = SendHttpMsgHandler.sendHttpsPost(postURL,null,certificateUrl,certificatePassword);
            //LOGGER.info(logInfo + "应用内支付响应|" + response);
            
            
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error(logInfo + "发送应用内支付请求|", e);
            throw LandseaException.createException(PaymentResultCode.E00150026, e);
        }
    }
    
    /**
     * 
     * [简要描述]：网页发送请求二维码</br>
     * [详细描述]：</br>
     * [作者]：Administrator(2017-07-23)</br>
     *
     * @param billPayment
     * @param helperObject
     * @param resultMap
     * @throws LandseaException
     *
     */
    public void sendPayRequestWeb(BillPayment billPayment, PaymentHelperBean helperObject,
        Map<String, Object> resultMap)
        throws LandseaException
    {
        // 获取日志前缀
        String logInfo = getLog(billPayment);
        
        try
        {
            // 地址
            String postURL = shijiPaymentConfig.getWebApiUrl();
            
            // 测试加密密钥
            String key = shijiPaymentConfig.getKey();
            
            // partnerId
            String partnerId = shijiPaymentConfig.getPartnerID();
            
            JsonObject postData = new JsonObject();
            
            // Generate order (Get QR Code) 预下单（获取二维码让手机扫）
            postData.addProperty("TxnType", "1");
            postData.addProperty("PartnerID", partnerId);
            
            String channelId = null;
            // 参见API文档
            if (PayType.SHIJI_WECHAT.equals(billPayment.getPayType()))
            {
                // 微信扫码
                channelId = "2";
            }
            else if (PayType.SHIJI_ALIPAY.equals(billPayment.getPayType()))
            {
                // 支付宝2.0扫码
                channelId = "6";
            }
            postData.addProperty("ChannelID", channelId);
            postData.addProperty("MerchantTxnNo", billPayment.getId());
            
            // 获取商户号
            String merchantId = getMerchantId(helperObject, false);
            postData.addProperty("MerchantID", merchantId);
            
            postData.addProperty("TxnAmt", LandSeaUtil.get2Double(billPayment.getMoney()));
            
            // 货币：CNY(编号为156)
            postData.addProperty("CurrencyCode", "156");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            String time = sdf.format(new Date());
            postData.addProperty("TxnReqTime", time);
            
            // 回调地址
            postData.addProperty("NotifyUrl", shijiPaymentConfig.getCallbackUrl());
            
            // 签名
            String sign = ShiJiSignVerifyUtil.sign(postData, key);
            postData.addProperty("Sign", sign);
            
            LOGGER.info(logInfo + "发送获取支付二维码请求|" + postData.toString());
            
            String response = SendHttpMsgHandler.sendHttpsPost(postURL,
                postData.toString(),
                CommonUtils.getResourceUrl(shijiPaymentConfig.getCertificate()),
                shijiPaymentConfig.getCertificatePassword());
            
            LOGGER.info(logInfo + "获取支付二维码响应|" + response);
            
            if (StringUtils.isEmpty(response))
            {
                throw LandseaException.createException(PaymentResultCode.E00150020);
            }
            
            // 解析返回数据
            JSONObject obj = JSONObject.fromObject(response);
            if (SUCCESS_CODE.equals(obj.getString("RespCode")))
            {
                String TxnAnsTime = obj.getString("TxnAnsTime");
                String QRCode = obj.getString("QRCode");
                String MerchantName = obj.getString("MerchantName");
                
                resultMap.put("QRCode", QRCode);
                resultMap.put("TxnAnsTime", TxnAnsTime);
                resultMap.put("MerchantName", MerchantName);
            }
            else
            {
                throw LandseaException.createException(PaymentResultCode.E00150020);
            }
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error(logInfo + "获取支付二维码失败|", e);
            throw LandseaException.createException(PaymentResultCode.E00150020, e);
        }
    }
    
    /**
     * 
     * [简要描述]：查询商户号</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-21)</br>
     *
     * @param helperObject
     * @param isMobilePay 是否为公众号支付 true是 false否
     * @return 商户号
     * @throws LandseaException
     *
     */
    private String getMerchantId(PaymentHelperBean helperObject, boolean isMobilePay)
        throws LandseaException
    {
        // 查询银石项目配置项
        ShiJiProjectConfig shiJiProjectConfig =
            shiJiPaymentService.getShiJiProjectConfig(helperObject.getProjectId(), helperObject.getContractId());
        
        if (null == shiJiProjectConfig)
        {
            throw LandseaException.createException(PaymentResultCode.E00150025);
        }
        
        // 网页扫码支付商户号
        String merchantId = shiJiProjectConfig.getMerchantId();
        
        // 公众号支付商户号
        String wechatMerchantId = shiJiProjectConfig.getWechatMerchantId();
        
        if (isMobilePay)
        {
        	if (StringUtils.isEmpty(wechatMerchantId))
        	{
        		LOGGER.error("getMerchantId|公众号内支付商户号为空");
        		throw LandseaException.createException(PaymentResultCode.E00150024);
        	}
        	return wechatMerchantId;
        }
        else
        {
        	if (StringUtils.isEmpty(merchantId))
        	{
        		LOGGER.error("getMerchantId|网页扫码支付商户号为空");
        		throw LandseaException.createException(PaymentResultCode.E00150024);
        	}
        	return merchantId;
        }
    }
    
    /**
     * 
     * [描述]：处理响应结果/查询支付结果</br>
     *
     * @param billPayment
     * @param paymentResponse
     * @param resultMap
     * @throws LandseaException
     *
     */
    @Override
    public boolean analysisResult(BillPayment billPayment, Object response, PaymentHelperBean helperObject,
        Map<String, Object> resultMap)
        throws LandseaException
    {
        // 获取日志前缀
        String logInfo = getLog(billPayment);
        
        // 账单已支付，直接返回成功
        if (BillPaymentStatus.SUCCESS.equals(billPayment.getStatus()))
        {
            LOGGER.info(logInfo + "账单已支付成功");
            resultMap.put("RespCode", "00");
            return PaymentConstant.RESPONSE_PROCESS_STOP;
        }
        
        ShiJiPaymentResponse paymentResponse = (ShiJiPaymentResponse)response;
        LOGGER.info(logInfo + "支付结果通知|" + paymentResponse.toString());
        
        // 第三方流水号(支付宝或者微信方)
        String channelTxnNo = paymentResponse.getChannelTxnNo();
        
        // 如果需要验签，做签名验证(主动查询支付结果时不需要验签)
        if (paymentResponse.isNeedValidateSign())
        {
            // 测试加密密钥
            String key = shijiPaymentConfig.getKey();
            
            // 验签
            if (!ShiJiSignVerifyUtil.verify(JsonObjectUtils.map2JsonObject(paymentResponse.getResponseMap()), key))
            {
                LOGGER.error(logInfo + "验签失败|" + paymentResponse.toString());
                throw LandseaException.createException(PaymentResultCode.E00150021);
            }
        }
        
        String responseCode = paymentResponse.getRespCode();
        if (StringUtils.isNotEmpty(responseCode))
        {
            // 当此参数不为空，则是主动查询支付结果，根据返回的实际支付结果设置状态
            if (SUCCESS_CODE.equals(responseCode))
            {
                resultMap.put("paymentStatus", BillPaymentStatus.SUCCESS);
                
                billPayment.setStatus(BillPaymentStatus.SUCCESS);
                billPayment.setTransactionNo(channelTxnNo);
                return PaymentConstant.RESPONSE_PROCESS_CONTINUE;
            }
            else
            {
                // 支付失败
                billPayment.setStatus(BillPaymentStatus.FAILED);
                // 失败的时候记录描述
                billPayment.setComments(paymentResponse.getRespDesc());
                
                // 更新支付记录表，支付失败，流程结束
                BillPayment paymentUpdate = new BillPayment();
                paymentUpdate.setId(billPayment.getId());
                paymentUpdate.setStatus(billPayment.getStatus());
                paymentUpdate.setTime(new Date());
                paymentUpdate.setComments(billPayment.getComments());
                // 添加公共字段
                paymentUpdate.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
                billPaymentWriteMapper.updateByPrimaryKeySelective(paymentUpdate);
                
                LOGGER.error(logInfo + "支付失败,只更新支付结果|" + paymentResponse.toString());
                
                // 要返回给页面
                resultMap.put("paymentStatus", BillPaymentStatus.FAILED);
                
                // 支付失败，流程不继续
                return PaymentConstant.RESPONSE_PROCESS_STOP;
            }
        }
        else
        {
            // 异步通知支付结果的，没有该参数，直接认为是成功的
            // 支付成功， 修改银行流水号，状态
            // 注：银石支付只会通知支付成功的订单
            billPayment.setStatus(BillPaymentStatus.SUCCESS);
            billPayment.setTransactionNo(channelTxnNo);
            
            return PaymentConstant.RESPONSE_PROCESS_CONTINUE;
        }
        
    }
    
    @Override
    public void paymentQuery(PaymentQueryRequest paymentQuery, BillPayment billPayment,
        PaymentQueryResult paymentQueryResult, Map<String, Object> resultMap)
        throws LandseaException
    {
        // 需要查询处理
        paymentQueryResult.setNeedQueryProcess(true);
        
        // 获取日志前缀
        String logInfo = getLog(billPayment);
        
        LOGGER.info(logInfo + "银石支付查询支付结果开始");
        
        try
        {
            // 地址
            String postURL = shijiPaymentConfig.getWebApiUrl();
            
            // 测试加密密钥
            String key = shijiPaymentConfig.getKey();
            
            // partnerId
            String partnerId = shijiPaymentConfig.getPartnerID();
            
            JsonObject postData = new JsonObject();
            
            // 查询支付结果
            postData.addProperty("TxnType", "5");
            postData.addProperty("PartnerID", partnerId);
            
            String channelId = null;
            // 参见API文档
            if (PayType.SHIJI_WECHAT.equals(billPayment.getPayType()))
            {
                // 微信扫码
                channelId = "2";
            }
            else if (PayType.SHIJI_ALIPAY.equals(billPayment.getPayType()))
            {
                // 支付宝2.0扫码
                channelId = "6";
            }
            else if (PayType.SHIJI_MOBILE.equals(billPayment.getPayType()))
            {
                // 微信
                channelId = "2";
            }
            postData.addProperty("ChannelID", channelId);
            
            postData.addProperty("OrgTxnNo", billPayment.getId());
            
            // 获取商户号
            String merchantId = getMerchantIdByContractId(paymentQuery.getContractId(), paymentQuery.getProjectId(), billPayment.getPayType());
            
            postData.addProperty("MerchantID", merchantId);
            postData.addProperty("TxnAmt", LandSeaUtil.get2Double(billPayment.getMoney()));
            
            // 货币：CNY(编号为156)
            postData.addProperty("CurrencyCode", "156");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            String time = sdf.format(new Date());
            postData.addProperty("TxnReqTime", time);
            
            // 签名
            String sign = ShiJiSignVerifyUtil.sign(postData, key);
            postData.addProperty("Sign", sign);
            
            LOGGER.info(logInfo + "发送查询支付结果请求|" + postData.toString());
            String response = SendHttpMsgHandler.sendHttpsPost(postURL,
                postData.toString(),
                CommonUtils.getResourceUrl(shijiPaymentConfig.getCertificate()),
                shijiPaymentConfig.getCertificatePassword());
            LOGGER.info(logInfo + "查询支付结果响应|" + response);
            
            if (StringUtils.isEmpty(response))
            {
                throw LandseaException.createException(PaymentResultCode.E00150028);
            }
            
            Map<String, Object> responseMap = JsonObjectUtils.jsonStr2map(response);
            
            // 将结果转换成对象
            ShiJiPaymentResponse shiJiPaymentResponse = shiJiPaymentService.buildResponseFromRespMap(responseMap);
            
            // 结果处理时不需要验签
            shiJiPaymentResponse.setNeedValidateSign(false);
            
            paymentQueryResult.setQueryResponseResult(shiJiPaymentResponse);
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error(logInfo + "查询支付结果失败|", e);
            throw LandseaException.createException(PaymentResultCode.E00150028, e);
        }
        
    }
    
    @Override
    public void buildResponseResult(BillPayment billPayment, PaymentHelperBean helperObject,
        Map<String, Object> resultMap)
        throws LandseaException
    {
        resultMap.put("RespCode", "00");
    }
    
    @Override
    protected String getProcessClassName()
    {
        return this.getClass().getSimpleName();
    }
    
    /**
     * 
     * [简要描述]：根据合同id查询商户号</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-24)</br>
     *
     * @param contractId
     * @return
     * @throws LandseaException
     *
     */
    private String getMerchantIdByContractId(Integer contractId, Integer projectId, String payTpye)
        throws LandseaException
    {
        try
        {
            PaymentHelperBean helperObject = new PaymentHelperBean();
            if (null != contractId)
            {
                // 查询合同
                Contract contract = contractReadMapper.selectByPrimaryKey(contractId);
                if (null != contract)
                {
                    helperObject.setContractId(contract.getId());
                    helperObject.setProjectId(contract.getProjectId());
                }
                else
                {
                    helperObject.setProjectId(projectId);
                }
            }
            else
            {
                helperObject.setProjectId(projectId);
            }
            
            // 商户号区分是web端扫码支付还是公众号里支付
            if (PayType.SHIJI_MOBILE.equals(payTpye))
            {
            	return getMerchantId(helperObject, true);
            }
            else
            {
            	return getMerchantId(helperObject, false);
            }
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("getMerchantIdByContractId|query failed. contractId=" + contractId, e);
            throw LandseaException.createException(PaymentResultCode.E00150027, e);
        }
    }
}
